﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

namespace Brain.Skinning
{
    public class SkinnedModel
    {
        /// <summary>
        /// The Model instance of this Skinned Model
        /// </summary>
        public Model Model { get; set; }

        /// <summary>
        /// All the Animations read from the Model file you imported
        /// </summary>
        public SkinnedAnimationClipDictionary AnimationClips { get; protected set; }

        public List<SkinnedBone> SkinnedBones { get; protected set; }

        /// <summary>
        /// The Current Skin Transforms
        /// </summary>
        public Matrix[] SkinTransforms;

        public Matrix[] BindPose { get; set; }

        internal SkinnedModel()
        {
        }

        internal static void Load(SkinnedModel SkinnedModel)
        {
            SkinnedModel.SkinTransforms = new Matrix[SkinnedModel.SkinnedBones.Count];

            for (int bone = 0; bone < SkinnedModel.SkinTransforms.Length; bone++)
            {
                SkinnedBone Bone = SkinnedModel.SkinnedBones[bone];
                SkinnedModel.SkinTransforms[bone] = Matrix.Identity;
            }
        }

        internal static SkinnedModel Read(ContentReader reader)
        {
            SkinnedModel skinnedModel = new SkinnedModel();

            skinnedModel.Model = reader.ReadObject<Model>();
            ReadSkinnedBones(skinnedModel, reader);
            ReadAnimationClips(skinnedModel, reader);
            ReadBoneIndices(skinnedModel, reader);

            Load(skinnedModel);

            return skinnedModel;
        }
        internal static void ReadAnimationClips(SkinnedModel model,
            ContentReader reader)
        {
            model.AnimationClips = new SkinnedAnimationClipDictionary();

            int animationCount = reader.ReadInt32();

            for (int i = 0; i < animationCount; i++)
            {
                string key = reader.ReadString();
                int duration = reader.ReadInt32();

                int boneCollections = reader.ReadInt32();
                Dictionary<int, BoneKeyFrameCollection> bones = new Dictionary<int, BoneKeyFrameCollection>();
                for (int k = 0; k < boneCollections; k++)
                {
                    int bone = reader.ReadInt32();

                    int count = reader.ReadInt32();
                    Frame_Skinned[] frames = new Frame_Skinned[count];
                    for (int l = 0; l < count; l++)
                    {
                        int time = reader.ReadInt32();
                        Vector3 pos = reader.ReadVector3();
                        Quaternion ori = reader.ReadQuaternion();
                        Vector3 sca = reader.ReadVector3();
                        frames[l] = new Frame_Skinned(time, pos, sca, ori);
                    }
                    BoneKeyFrameCollection col = new BoneKeyFrameCollection(model, bone, frames);
                    bones.Add(col.BoneIndex, col);
                }

                SkinnedAnimationClip clip = new SkinnedAnimationClip(duration, bones);
                model.AnimationClips.Add(key, clip);
            }
        }
        internal static void ReadSkinnedBones(SkinnedModel model,
            ContentReader reader)
        {
            model.SkinnedBones = new List<SkinnedBone>();

            int bonesCount = reader.ReadInt32();
            for (int i = 0; i < bonesCount; i++)
            {
                Matrix invert = reader.ReadMatrix();
                int parentBones = reader.ReadInt32();
                string name = reader.ReadString();
                model.SkinnedBones.Add(new SkinnedBone(invert, parentBones, name));
            }
        }
        internal static void ReadBoneIndices(SkinnedModel model,
            ContentReader reader)
        {
            int bindPose = reader.ReadInt32();
            model.BindPose = new Matrix[bindPose];
            for (int i = 0; i < bindPose; i++)
            {
                Matrix mat = reader.ReadMatrix();
                model.BindPose[i] = mat;
            }
        }
    }
}
